/*
 * @Descripttion: 
 * @version: 
 * @Author: Lee
 * @Date: 2020-05-25 15:03:22
 * @LastEditors: Please set LastEditors
 * @LastEditTime: 2020-05-27 17:03:27
 */
import Watcher from "./watcher";
export default class Compiler {
    constructor(context) {
        this.$el = context.$el;
        this.context = context;
        if (this.$el) {
            // 把原始的DOM转换为文档片段
            // 输出 #document-fragment <p>{{msg}}</p><p v-text="msg"></p>
            this.$fragment = this.nodeToFragment(this.$el);
            //添加编译
            this.compiler(this.$fragment);
            this.$el.appendChild(this.$fragment)
        }
    }
    /**
     * @name: 把所有的元素转换为文档片段
     * @test: test font
     * @msg: 
     * @param {type} 
     * @return: 
     */

    nodeToFragment(node) {
        // 创建一个文档片段
        let fragment = document.createDocumentFragment();
        // 加入元素有子节点且长度大于等于1
        if (node.childNodes && node.childNodes.length) {
            node.childNodes.forEach(child => {
                if (!this.ignoreable(child)) {
                    fragment.appendChild(child);
                }
            })
        }
        return fragment;
    }

    /**
     * @name: 忽略注释、转行符等节点
     * @test: test font
     * @msg: 
     * @param {type} 
     * @return: 
     */
    ignoreable(node) {
        var reg = /^[\t\n\r]+/;
        return (
            node.nodeType === 8 || (node.nodeType === 3 && reg.test(node.textContent))
        );
    }

    /**
     * @name: 模板编译
     * @test: test font
     * @msg: 
     * @param {type} 
     * @return: 
     */

    compiler(node) {
        if (node.childNodes && node.childNodes.length) {
            node.childNodes.forEach(child => {
                if (child.nodeType === 1) {
                    // 当nodeType为1时说明是元素节点
                    this.compilerElementNode(child)
                } else if (child.nodeType === 3) {
                    // 当nodeType为3时说明是文本节点
                    this.compilerTextNode(child);
                }
            })
        }

    }

    /**
     * @name: 编译元素节点
     * @test: test font
     * @msg: 
     * @param {type} 
     * @return: 
     */
    compilerElementNode(node) {
        // todo 完成元素的编译
        // todo 指令等
        let that = this;
        let attrs = [...node.attributes];
        attrs.forEach(attr => {
            // 解构赋值 attr.name = v-text; attr.value = msg;
            let {
                name: attrName,
                value: attrValue
            } = attr;
            if (attrName.indexOf("v-") === 0) {
                // 截取指令前两位
                let dirName = attrName.slice(2);
                switch (dirName) {
                    case "text":
                        new Watcher(attrValue, this.context, newValue => {
                            node.textContent = newValue;
                        })
                        break;
                    case "model":
                        new Watcher(attrValue, this.context, newValue => {
                            node.value = newValue;
                        })
                        node.addEventListener("input", e => {
                            that.context[attrValue] = e.target.value;
                        })
                        break;
                }
            }
            if (attrName.indexOf('@') === 0) {
                this.compilerMethods(this.context, node, attrName, attrValue)
            }
        })
        this.compiler(node);
    }


    /**
     * @name: 编译函数
     * @test: test font
     * @msg: 
     * @param {type} 
     * @return: 
     */
    compilerMethods(scope, node, attrName, attrValue) {
        let type = attrName.slice(1)
        let fn = scope[attrValue]
        node.addEventListener(type, fn.bind(scope))
    }

    /**
     * @name: 编译文本节点
     * @test: test font
     * @msg: 
     * @param {type} 
     * @return: 
     */
    compilerTextNode(node) {
        let text = node.textContent.trim();
        if (text) {
            // 把text字符串，转换成表达式
            let exp = this.parseTextExp(text);
            // 添加订阅者，计算表达式的值
            // 当表达式依赖的数据发生变化时
            // 1、重新计算表达式的值、
            // 2、node.textContent给最新的值
            // 即可完成Model -> View的响应式
            new Watcher(exp, this.context, (newValue) => {
                node.textContent = newValue;

            })


        }
    }

    /**
     * @name: 该函数完成了文本到表达式的转换
     * @test: test font
     * @msg: 111{{msg + '.....'}}222 -> "111" + (msg + ".....") + "222"
     * @param {type} 
     * @return: 
     */
    parseTextExp(text) {
        // 匹配差值表达式正则
        let regText = /\{\{(.+?)\}\}/g;
        // 分割差值表达式前后内容
        let pices = text.split(regText);
        // 匹配差值表达式
        let matches = text.match(regText);
        // 表达式数组
        let tokens = [];
        pices.forEach(item => {
            if (matches && matches.indexOf("{{" + item + "}}") > -1) {
                tokens.push("(" + item + ")");
            } else {
                tokens.push("`" + item + "`");
            }
        });
        return tokens.join("+");
    }
}